#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

const int max_cap = 5;

template <class T>
class blockQueue
{
public:
    blockQueue(int cap = max_cap)
        : _cap(cap)
    {
        // 初始化互斥量和条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_consumer_cond, nullptr);
        pthread_cond_init(&_productor_cond, nullptr);
    }
    ~blockQueue()
    {
        // 销毁互斥量和条件变量
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_consumer_cond);
        pthread_cond_destroy(&_productor_cond);
    }

    bool isFull() { return _q.size() == _cap; }
    bool isEmpty() { return _q.empty(); }

    // productor call
    void push(const T &input)
    {
        pthread_mutex_lock(&_mutex);

        while (isFull()) // 阻塞队列为满, 不push, 等待
        {
            pthread_cond_wait(&_productor_cond, &_mutex);
        }

        _q.push(input);
        // 此时阻塞队列中至少有一个数据，唤醒消费者
        pthread_cond_signal(&_consumer_cond);

        pthread_mutex_unlock(&_mutex);
    }

    // consumer call
    void pop(T *output)
    {
        pthread_mutex_lock(&_mutex);

        while (isEmpty())
        {
            pthread_cond_wait(&_consumer_cond, &_mutex);
        }

        *output = _q.front();
        _q.pop();
        // 此时阻塞队列中至少有一个空位, 唤醒生产者
        pthread_cond_signal(&_productor_cond);

        pthread_mutex_unlock(&_mutex);
    }

private:
    std::queue<T> _q; // 阻塞队列
    int _cap;         // 阻塞队列的最大容量

    // 因为生产-生产, 消费-消费, 生产-消费的都是访问同一个缓冲区, 所以用同一把锁
    pthread_mutex_t _mutex;

    // 生产者和消费者的等待条件不同, 因此条件变量需各有一个
    pthread_cond_t _consumer_cond;
    pthread_cond_t _productor_cond;
};